<template>
    <div class="my-cropper" ref="myCropper" @mousewheel.stop.prevent="mouseWheel">
        <!--原图区域-->
        <div class="original-box">
            <div class="original-image-box" :style="{
                'width': imgBox.width + 'px',
                'height': imgBox.height + 'px',
                'transform': imgBoxStyle
            }">
                <img :src="originImage" />
            </div>
        </div>
        <!--阴影-->
        <div class="outer-shadow" @mousedown.stop.prevent="mouseDownShadow"></div>

        <!--切图区域-->
        <div class="cut-box" @mousedown.stop.prevent="mouseDownCut" :style="{
            'width': cutBox.width + 'px',
            'height': cutBox.height + 'px',
            'transform': `translate(${cutBox.moveLeft}px, ${cutBox.moveTop}px)`
        }">
            <div class="resolution" v-if="showResolution">
                {{ cutBox.width }} × {{ cutBox.height }}
            </div>
            <div class="cut-image-box">
                <img :src="originImage" :style="{
                    'width': imgBox.width + 'px',
                    'height': imgBox.height + 'px',
                    'transform': cutImgStyle
                }" />
            </div>
            <div v-if="enableCut">
                <!--操作线-->
                <div class="action-line left-line" @mousedown.stop.prevent="(e) => changeSideDown(e, 'left')"></div>
                <div class="action-line top-line" @mousedown.stop.prevent="(e) => changeSideDown(e, 'top')"></div>
                <div class="action-line right-line" @mousedown.stop.prevent="(e) => changeSideDown(e, 'right')"></div>
                <div class="action-line bottom-line" @mousedown.stop.prevent="(e) => changeSideDown(e, 'bottom')"></div>
                <!--操作球-->
                <!--顶部-->
                <div class="action-point top-left-point" @mousedown.stop.prevent="(e) => changeSideDown(e, 'top-left')">
                </div>
                <div class="action-point top-center-point" @mousedown.stop.prevent="(e) => changeSideDown(e, 'top')">
                </div>
                <div class="action-point top-right-point" @mousedown.stop.prevent="(e) => changeSideDown(e, 'top-right')">
                </div>
                <!--右侧-->
                <div class="action-point right-center-point" @mousedown.stop.prevent="(e) => changeSideDown(e, 'right')">
                </div>
                <div class="action-point right-bottom-point"
                    @mousedown.stop.prevent="(e) => changeSideDown(e, 'bottom-right')"></div>
                <!--下侧-->
                <div class="action-point bottom-center-point" @mousedown.stop.prevent="(e) => changeSideDown(e, 'bottom')">
                </div>
                <div class="action-point bottom-left-point"
                    @mousedown.stop.prevent="(e) => changeSideDown(e, 'bottom-left')"></div>
                <!--左侧-->
                <div class="action-point left-center-point" @mousedown.stop.prevent="(e) => changeSideDown(e, 'left')">
                </div>
            </div>
        </div>
    </div>
</template>
<script lang="ts" setup>
import { onMounted, onBeforeUnmount, watch, computed, reactive, ref, type Ref, nextTick } from 'vue';
import { imgBox, outerBox, cutBox, changeImage, generateImg } from "./info-init";
// 定义入参
const props = defineProps({

    // 原图
    originImage: {
        type: String,
        default: ""
    },
    // 展示分辨率
    showResolution: {
        type: Boolean,
        default: true
    },
    // 能否改变切图区域尺寸
    enableCut: {
        type: Boolean,
        default: true
    },
    // 切图区域默认的宽度
    defaultCutWidth: {
        type: Number,
        default: 0
    },
    // 切图区域默认的高度
    defaultCutHeight: {
        type: Number,
        default: 0
    },
})

// 原图的内联样式
const imgBoxStyle = computed(() => {
    return `translate(${imgBox.moveLeft}px, ${imgBox.moveTop}px) rotateZ(${imgBox.rotate})`
})

// 切块图的内联样式
const cutImgStyle = computed(() => {
    let leftPosition = imgBox.moveLeft - cutBox.moveLeft
    let topPosition = imgBox.moveTop - cutBox.moveTop
    return `translate(${leftPosition}px, ${topPosition}px) rotateZ(${imgBox.rotate})`
})

// 鼠标按下事件全局
const mouseDown = reactive({
    event: null as any,

    // cutBox 偏移量
    cutBoxMoveLeft: 0,
    cutBoxMoveTop: 0,

    // changeSide专用
    position: '',
    // 鼠标按下的时候，那时候 cutBox的宽高
    downCutBoxWidth: 0,
    downCutBoxHeight: 0,

    // 原图移动 阴影区
    imgBoxMoveLeft: 0,
    imgBoxMoveTop: 0
})

//定义回调
const emit = defineEmits(["getImg"]);

// 外框
const myCropper: any = ref(null) as Ref<Element | unknown>

// 获得外框
const getOuterBoxInfo = () => {

    nextTick(() => {
        const bigDom: any = myCropper.value

        //offsetWidth 不能得到小数点后数字
        outerBox.width = parseFloat(window.getComputedStyle(bigDom).width)
        outerBox.height = parseFloat(window.getComputedStyle(bigDom).height)

        // 获得外框信息
        getCutBoxInfo()
    })

}

// 收缩边框的操作点击
const changeSideDown = (e: any, position: string) => {
    mouseDown.event = e

    mouseDown.cutBoxMoveLeft = cutBox.moveLeft

    mouseDown.cutBoxMoveTop = cutBox.moveTop

    mouseDown.downCutBoxWidth = cutBox.width

    mouseDown.downCutBoxHeight = cutBox.height

    mouseDown.position = position

    window.addEventListener("mousemove", changeSideMove);

    window.addEventListener("mouseup", mouseUp);
}
// 收缩边框的操作移动
const changeSideMove = (e: any) => {
    const moveX = e.clientX
    const moveY = e.clientY

    const disX = mouseDown.event.clientX - mouseDown.cutBoxMoveLeft

    const disY = mouseDown.event.clientY - mouseDown.cutBoxMoveTop

    //实际的偏移量
    let nX = moveX - disX
    let nY = moveY - disY

    //按下值 与 移动值 的差值
    let diffX = moveX - mouseDown.event.clientX
    let diffY = moveY - mouseDown.event.clientY

    //移动方向
    const position = mouseDown.position

    // 鼠标按下的时候，cutBox改变前的宽高值
    const downWidth = mouseDown.downCutBoxWidth

    const downHeight = mouseDown.downCutBoxHeight

    // 鼠标按下的时候，cutBox改变前的上下偏移量
    const downMoveLeft = mouseDown.cutBoxMoveLeft

    const downMoveTop = mouseDown.cutBoxMoveTop

    //左边移动
    const doMoveLeft = () => {
        cutBox.width = Math.abs(downWidth - diffX)

        // 反转控制
        if (downWidth - diffX <= 0) {
            //如果宽度已经为0 那么偏移量将不再增加
            cutBox.moveLeft = downMoveLeft + downWidth
        } else {
            cutBox.moveLeft = nX
        }

        // 边界控制
        if (downWidth - diffX <= 0 && nX >= outerBox.width) {
            // 反转的情况下，碰到了边界
            // 这种情况下 this.cutBox.moveLeft 永远是同一个值 所以宽度只需要 外框 - 偏移量 就能得到临界值
            cutBox.width = outerBox.width - cutBox.moveLeft
        }
        else if (nX <= 0) {
            //普通情况下碰到了边界
            cutBox.width = Math.abs(downWidth + downMoveLeft)
            cutBox.moveLeft = 0
        }
    }

    //右边移动
    const doMoveRight = () => {
        cutBox.width = Math.abs(downWidth + diffX)

        if (downWidth + diffX <= 0) {
            cutBox.moveLeft = downMoveLeft - cutBox.width
        } else {
            cutBox.moveLeft = downMoveLeft
        }

        // 边界控制
        if (downWidth + diffX <= 0 && cutBox.moveLeft <= 0) {
            cutBox.moveLeft = 0
            cutBox.width = downMoveLeft - cutBox.moveLeft
        } else if (cutBox.moveLeft + cutBox.width >= outerBox.width) {
            cutBox.width = outerBox.width - downMoveLeft
        }
    }

    //上移动
    const doMoveTop = () => {
        cutBox.height = Math.abs(downHeight - diffY)

        // 反转控制
        if (downHeight - diffY <= 0) {
            //如果宽度已经为0 那么偏移量将不再增加
            cutBox.moveTop = downMoveTop + downHeight
        } else {
            cutBox.moveTop = nY
        }

        // 边界控制
        if (downHeight - diffY <= 0 && nY >= outerBox.height) {
            cutBox.height = outerBox.height - cutBox.moveTop
        } else if (nY <= 0) {
            cutBox.moveTop = 0
            cutBox.height = downHeight + downMoveTop
        }
    }

    //下移动
    const doMoveBottom = () => {
        cutBox.height = Math.abs(downHeight + diffY)

        // 反转控制
        if (downHeight + diffY <= 0) {
            cutBox.moveTop = downMoveTop - cutBox.height
        } else {
            cutBox.moveTop = downMoveTop
        }

        // 边界控制
        if (downHeight + diffY <= 0 && cutBox.moveTop <= 0) {
            cutBox.moveTop = 0
            cutBox.height = downMoveTop - cutBox.moveTop
        } else if (cutBox.moveTop + cutBox.height >= outerBox.height) {
            cutBox.height = outerBox.height - downMoveTop
        }
    }

    switch (position) {
        case 'left':
            doMoveLeft()
            break
        case 'bottom':
            doMoveBottom()
            break;
        case 'right':
            doMoveRight()
            break
        case 'top':
            doMoveTop()
            break
        case 'top-left':
            doMoveTop()
            doMoveLeft()
            break
        case 'top-right':

            doMoveTop()
            doMoveRight()

            break
        case 'bottom-left':
            doMoveBottom()
            doMoveLeft()
            break
        case 'bottom-right':
            doMoveBottom()
            doMoveRight()
            break
    }

    // 生成新图
    generateImg(props.originImage, (base64: string) => {
        // 回调事件会产生新图的base64
        emit('getImg', base64)
    })

}

const mouseWheel = (e: any) => {
    let wheelValue = e.deltaY

    //大于0 向上走
    if (wheelValue > 0) {
        //不能再缩小了
        if (imgBox.scale > 0.1) {
            imgBox.scale -= 0.1
        }
    } else {
        imgBox.scale += 0.1
    }
    imgBox.scale = Number(imgBox.scale.toFixed(1))

    // 初始宽度
    const oldWidth = imgBox.initWidth

    const oldHeight = imgBox.initHeight

    // 在改变前 上一个宽度
    const lastWidth = imgBox.width

    // 在改变前 上一个高度
    const lastHeight = imgBox.height

    imgBox.width = oldWidth * imgBox.scale

    imgBox.height = oldHeight * imgBox.scale

    // 偏移量设置 让图居中显示
    imgBox.moveLeft = imgBox.moveLeft + (lastWidth - imgBox.width) / 2

    imgBox.moveTop = imgBox.moveTop + (lastHeight - imgBox.height) / 2

    // 生成新图
    generateImg(props.originImage, (base64: string) => {
        // 回调事件会产生新图的base64
        emit('getImg', base64)
    })
}

const mouseDownCut = (e: any) => {
    mouseDown.event = e

    mouseDown.cutBoxMoveLeft = cutBox.moveLeft

    mouseDown.cutBoxMoveTop = cutBox.moveTop


    window.addEventListener("mousemove", mouseCutMove);

    window.addEventListener("mouseup", mouseUp);
}

const mouseDownShadow = (e: any) => {
    mouseDown.event = e

    mouseDown.imgBoxMoveLeft = imgBox.moveLeft

    mouseDown.imgBoxMoveTop = imgBox.moveTop
    window.addEventListener("mousemove", mouseMoveShadow);

    window.addEventListener("mouseup", mouseUp);
}

const mouseMoveShadow = (e: any) => {
    const moveX = e.clientX
    const moveY = e.clientY

    const disX = mouseDown.event.clientX

    const disY = mouseDown.event.clientY

    //实际的偏移量
    let nX = moveX - disX
    let nY = moveY - disY


    imgBox.moveLeft = mouseDown.imgBoxMoveLeft + nX
    imgBox.moveTop = mouseDown.imgBoxMoveTop + nY

    // 生成新图
    generateImg(props.originImage, (base64: string) => {
        // 回调事件会产生新图的base64
        emit('getImg', base64)
    })

}


// 切图框移动
const mouseCutMove = (e: any) => {

    const disX = mouseDown.event.clientX - mouseDown.cutBoxMoveLeft

    const disY = mouseDown.event.clientY - mouseDown.cutBoxMoveTop

    let nX = e.clientX - disX
    let nY = e.clientY - disY

    // 左大框边界
    if (nX <= 0) {
        nX = 0
    }
    if (nY <= 0) {
        nY = 0
    }

    // 右大框边界限制
    if (nX >= outerBox.width - cutBox.width) {
        nX = outerBox.width - cutBox.width
    }
    if (nY >= outerBox.height - cutBox.height) {
        nY = outerBox.height - cutBox.height
    }

    cutBox.moveLeft = nX

    cutBox.moveTop = nY

    // 生成新图
    generateImg(props.originImage, (base64: string) => {
        // 回调事件会产生新图的base64
        emit('getImg', base64)
    })
}

// 获得切块的信息  该信息通过外框来设置
// 若是切图区域没有默认宽高信息，或者它们默认为0 则取原图大小/4
const getCutBoxInfo = () => {

    if (!props.defaultCutWidth) {
        cutBox.width = outerBox.width / 4
    } else {
        cutBox.width = props.defaultCutWidth
    }

    if (!props.defaultCutHeight) {
        cutBox.height = outerBox.height / 4
    } else {
        cutBox.height = props.defaultCutHeight
    }


    //偏移量设置 让图居中显示
    cutBox.moveLeft = (outerBox.width - cutBox.width) / 2

    cutBox.moveTop = (outerBox.height - cutBox.height) / 2

    // 计算比例
    cutBox.scale = cutBox.width / cutBox.height
}

// 鼠标松开 清空事件
const mouseUp = () => {
    window.removeEventListener("mousemove", mouseCutMove);
    window.removeEventListener("mousemove", changeSideMove);
    window.removeEventListener("mousemove", mouseMoveShadow);
    window.removeEventListener("mouseup", mouseUp);
}

watch(() => props.originImage,
    (v) => {
        // 原图的位置调整
        changeImage(v, (base64: string) => {
            // 回调事件会产生新图的base64
            emit('getImg', base64)
        })
    })

onMounted(() => {
    // 获得外框
    getOuterBoxInfo()
    if (props.originImage) {
        // 原图的位置调整
        changeImage(props.originImage, (base64: string) => {
            // 回调事件会产生新图的base64
            emit('getImg', base64)
        })
    }
})

onBeforeUnmount(() => {
    // 清空事件
    mouseUp()
})


</script>

<style scoped lang="scss">
@mixin fullShow {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    user-select: none;
}

.my-cropper {
    position: relative;
    width: 100%;
    height: 100%;
    box-sizing: border-box;
    user-select: none;
    background-image: url();

    .outer-shadow {
        cursor: move;
        @include fullShow;
        background-color: rgba(0, 0, 0, .5);
    }

    .original-box {
        overflow: hidden;
        @include fullShow;

        .original-image-box {
            @include fullShow;

            img {
                user-select: none;
                -webkit-user-drag: none;
                //max-width: 100%;
                height: 100%;
            }
        }
    }

    .cut-box {
        @include fullShow;
        cursor: move;
        background-color: rgba(255, 255, 255, 0.2);
        outline: 1px solid rgba(51, 153, 255, .75);

        .resolution {
            padding: 0 4px;
            position: absolute;
            font-size: 12px;
            top: 0;
            left: 0;
            transform: translateY(-100%);
            color: #ffffff;
            background-color: rgba(0, 0, 0, 0.4);
        }

        .cut-image-box {
            height: 100%;
            width: 100%;
            overflow: hidden;

            img {
                user-select: none;
                -webkit-user-drag: none;
            }
        }

        // 操作线的粗细
        $lineBold: 5px;
        // 线冒出框框外一点
        $linePosition: -3px;

        .action-line {
            position: absolute;

            &.left-line {
                cursor: w-resize;
                top: 0;
                left: $linePosition;
                width: $lineBold;
                height: 100%;
            }

            &.top-line {
                cursor: n-resize;
                top: $linePosition;
                left: 0;
                height: $lineBold;
                width: 100%;
            }

            &.right-line {
                cursor: e-resize;
                top: 0;
                right: $linePosition;
                height: 100%;
                width: $lineBold;
            }

            &.bottom-line {
                cursor: s-resize;
                bottom: $linePosition;
                left: 0;
                height: $lineBold;
                width: 100%;
            }
        }

        // 球向外移动的位置
        $pointPosition: -4px;

        // 操作球
        .action-point {
            position: absolute;
            background-color: rgba(51, 153, 255, .75);
            width: 8px;
            height: 8px;
            border-radius: 100%;

            &.top-left-point {
                top: $pointPosition;
                left: $pointPosition;
                cursor: nw-resize;
            }

            &.top-center-point {
                top: $pointPosition;
                right: 0;
                left: 0;
                margin: 0 auto;
                cursor: n-resize;
            }

            &.top-right-point {
                top: $pointPosition;
                right: $pointPosition;
                cursor: ne-resize;
            }

            &.right-center-point {
                top: 0;
                bottom: 0;
                right: $pointPosition;
                margin: auto 0;
                cursor: e-resize;
            }

            &.right-bottom-point {
                bottom: $pointPosition;
                right: $pointPosition;
                cursor: se-resize;
            }

            &.bottom-center-point {
                bottom: $pointPosition;
                right: 0;
                left: 0;
                margin: 0 auto;
                cursor: s-resize;
            }

            &.bottom-left-point {
                bottom: $pointPosition;
                left: $pointPosition;
                cursor: sw-resize;

            }

            &.left-center-point {
                left: $pointPosition;
                top: 0;
                bottom: 0;
                margin: auto 0;
                cursor: w-resize;
            }
        }
    }
}
</style>
